use std::sync::Arc;

use common_base::{async_recursion::async_recursion, prelude::*};

use crate::{
    data::{DataObject, ParamObject, ResponseObject, ResponsePayload},
    db_manager::DbConnection,
    invoker::InvokerName,
    InvokerManager, ServiceComponent, ServiceContext, ServiceContextBase,
};

use super::{service_component_manager::ServiceComponentRunType, service_model::ServiceContainer};

pub struct ServiceExecutorManager;

impl ServiceExecutorManager {
    pub async fn sync_run_service(
        srv_cntr: Arc<ServiceContainer>,
        param_object: ParamObject,
        data_object: Option<DataObject>,
        /*customObject,  addonObject, */
        connection: Option<DbConnection>,
        response_object: ResponseObject,
    ) -> Result<ResponseObject> {
        let mut srv_ctx = Self::create_context(
            srv_cntr.clone(),
            param_object,
            data_object,
            connection,
            response_object,
            ServiceComponentRunType::Sync,
        )
        .await;

        let response = srv_cntr
            .service_context_builder
            .create_response(srv_ctx.as_mut())
            .await?;
        let data_model = response;

        // TODO: serviceContainer.isScriptable()

        srv_ctx
            .get_service_context_base_mut()
            .response_object
            .payload = Some(ResponsePayload::Landing(data_model));

        // set license to response object

        // monitor service start

        let result = Self::run_service(srv_cntr.clone(), srv_ctx.as_mut())
            .await
            .map_err(|e| {
                // error process
                e
            });

        // monitor service end

        if let Err(e) = Self::finish(srv_cntr.clone(), srv_ctx.as_mut()).await {
            warn!(
                "after sync_run_service, the finish stage process is failed, although we ignore this error and return Ok, cause: {}",
                e
            );
        }

        if let Ok(_) = result {
            Ok(std::mem::take(
                &mut srv_ctx.get_service_context_base_mut().response_object,
            ))
        } else {
            Err(result.unwrap_err())
        }
    }

    async fn create_context(
        srv_cntr: Arc<ServiceContainer>,
        param_object: ParamObject,
        data_object: Option<DataObject>,
        /*customObject,  addonObject, */
        connection: Option<DbConnection>,
        response_object: ResponseObject,
        run_type: ServiceComponentRunType,
    ) -> Box<dyn ServiceContext> {
        let context_base = ServiceContextBase::new(
            param_object,
            data_object,
            connection,
            response_object,
            run_type,
        );

        let context = srv_cntr
            .service_context_builder
            .new_context(context_base)
            .await;

        if run_type == ServiceComponentRunType::Async {
            // context.set_auto_connection(false);
            // context
            //     .get_service_context_base_mut()
            //     .param_object_mut()
            //     .remove_connection(connection);
        }

        context
    }

    async fn run_service(
        srv_cntr: Arc<ServiceContainer>,
        srv_ctx: &mut dyn ServiceContext,
    ) -> Result<()> {
        let tran_type = srv_ctx.get_int_by_param_name("stageTran", -1);
        let tran_type = if tran_type == -1 {
            srv_cntr.stage_tran
        } else {
            tran_type
        };

        match tran_type {
            0 => Self::run_service_tran_0(srv_cntr, srv_ctx).await,
            1 => Self::run_service_tran_1(srv_cntr, srv_ctx).await,
            _ => todo!(),
        }
    }

    async fn run_service_tran_0(
        srv_cntr: Arc<ServiceContainer>,
        srv_ctx: &mut dyn ServiceContext,
    ) -> Result<()> {
        if srv_cntr.before_prepare_comp {
            Self::process_prepare_service(srv_cntr.clone(), srv_ctx).await?;
        }

        if let Some(prepare_srv_comp) = srv_cntr.prepare_service_component.as_ref() {
            Self::run_service_component(prepare_srv_comp.clone(), srv_cntr.clone(), srv_ctx)
                .await?;
        }

        if let Some(process_srv_comp) = srv_cntr.process_service_component.as_ref() {
            Self::run_service_component(process_srv_comp.clone(), srv_cntr.clone(), srv_ctx)
                .await?;
        }

        if let Some(finish_srv_comp) = srv_cntr.finish_service_component.as_ref() {
            Self::run_service_component(finish_srv_comp.clone(), srv_cntr.clone(), srv_ctx).await?;
        }

        Self::process_finish_service(srv_cntr, srv_ctx).await?;

        Ok(())
    }

    /// 自动提交事务
    async fn run_service_tran_1(
        srv_cntr: Arc<ServiceContainer>,
        srv_ctx: &mut dyn ServiceContext,
    ) -> Result<()> {
        // if srv_ctx.param_obj.connection is not null
        // set auto commit

        if srv_cntr.before_prepare_comp {
            Self::process_prepare_service(srv_cntr.clone(), srv_ctx).await?;
        }

        if let Some(prepare_srv_comp) = srv_cntr.prepare_service_component.as_ref() {
            Self::run_service_component(prepare_srv_comp.clone(), srv_cntr.clone(), srv_ctx)
                .await?;
        }

        if let Some(process_srv_comp) = srv_cntr.process_service_component.as_ref() {
            Self::run_service_component(process_srv_comp.clone(), srv_cntr.clone(), srv_ctx)
                .await?;
        }

        if let Some(finish_srv_comp) = srv_cntr.finish_service_component.as_ref() {
            Self::run_service_component(finish_srv_comp.clone(), srv_cntr.clone(), srv_ctx).await?;
        }

        Self::process_finish_service(srv_cntr, srv_ctx).await?;

        Ok(())
    }

    #[async_recursion]
    async fn process_prepare_service(
        srv_cntr: Arc<ServiceContainer>,
        srv_ctx: &mut dyn ServiceContext,
    ) -> Result<()> {
        if srv_cntr.prepare_service.is_none() {
            return Ok(());
        }

        let prepare_service = srv_cntr.prepare_service.as_ref().unwrap().trim();
        if prepare_service.is_empty() {
            return Ok(());
        }

        let pre_serv_params = srv_cntr
            .service_context_builder
            .get_prepare_service_params(srv_ctx);
        if pre_serv_params.is_none() {
            return Ok(());
        }

        let mut pre_serv_params = pre_serv_params.unwrap();
        pre_serv_params
            .0
            .set_value_by_env_name("ServiceKey", prepare_service.trim().to_owned());

        let ro = InvokerManager::iom(
            InvokerName::ServiceComponentInvoker,
            pre_serv_params.0,
            pre_serv_params.1,
        )
        .await?;

        return srv_cntr
            .service_context_builder
            .process_prepare_service_response(srv_ctx, ro)
            .await;
    }

    #[async_recursion]
    async fn run_service_component(
        service_component: Arc<dyn ServiceComponent>,
        srv_cntr: Arc<ServiceContainer>,
        srv_ctx: &mut dyn ServiceContext,
    ) -> Result<()> {
        // TODO: monitorServiceComponent start

        if service_component.get_service_component_base().enabled {
            // we do not support plugin function custom defined by module

            service_component
                .run_prepare_plugin_service(srv_ctx)
                .await?;

            service_component
                .run_process_plugin_service(srv_ctx)
                .await?;

            service_component.run_finish_plugin_service(srv_ctx).await?;
        }

        // TOOD: monitorServiceComponent finish

        let next = service_component
            .get_service_component_base()
            .next_service_component
            .as_ref();
        if next.is_none() {
            return Ok(());
        }
        let next = next.unwrap();

        Self::run_service_component(next.clone(), srv_cntr, srv_ctx).await?;

        Ok(())
    }

    #[async_recursion]
    async fn process_finish_service(
        srv_cntr: Arc<ServiceContainer>,
        srv_ctx: &mut dyn ServiceContext,
    ) -> Result<()> {
        if srv_cntr.finish_service.is_none() {
            return Ok(());
        }

        let finish_service = srv_cntr.finish_service.as_ref().unwrap().trim();
        if finish_service.is_empty() {
            return Ok(());
        }

        let fin_srv_params = srv_cntr
            .service_context_builder
            .get_finish_service_params(srv_ctx);
        if fin_srv_params.is_none() {
            return Ok(());
        }
        let mut fin_srv_params = fin_srv_params.unwrap();

        fin_srv_params
            .0
            .set_value_by_env_name("ServiceKey", finish_service.trim().to_owned());

        let ro = InvokerManager::iom(
            InvokerName::ServiceComponentInvoker,
            fin_srv_params.0,
            fin_srv_params.1,
        )
        .await?;

        return srv_cntr
            .service_context_builder
            .process_finish_service_response(srv_ctx, ro)
            .await;
    }

    async fn finish(
        srv_cntr: Arc<ServiceContainer>,
        srv_ctx: &mut dyn ServiceContext,
        // response: &DataModel,
    ) -> Result<()> {
        // srv_ctx.set_script_manager(None);
        srv_cntr
            .service_context_builder
            .finish_context(srv_ctx)
            .await

        // if let Some(name_context) = srv_ctx.get_name_context() {
        //     name_context.close();
        // }
    }
}
